
package com.airbnb.epoxy;

import com.airbnb.epoxy.ViewHolderState.ViewState;
import com.harmony.recyclerview.collection.LongSparseArray;
import com.harmony.recyclerview.collection.SparseArray;
import ohos.agp.components.Component;
import ohos.utils.Parcel;
import ohos.utils.Sequenceable;

import java.util.Collection;

/**
 * Helper for {@link EpoxyAdapter} to store the state of Views in the adapter. This is useful for
 * saving changes due to user input, such as text input or selection, when a view is scrolled off
 * screen or if the adapter needs to be recreated.
 * <p/>
 * This saved state is separate from the state represented by a {@link EpoxyModel}, which should
 * represent the more permanent state of the data shown in the view. This class stores transient
 * state that is added to the View after it is bound to a {@link EpoxyModel}. For example, a {@link
 * EpoxyModel} may inflate and bind an EditText and then be responsible for styling it and attaching
 * listeners. If the user then inputs text, scrolls the view offscreen and then scrolls back, this
 * class will preserve the inputted text without the {@link EpoxyModel} needing to be aware of its
 * existence.
 * <p/>
 * This class relies on the adapter having stable ids, as the state of a view is mapped to the id of
 * the {@link EpoxyModel}.
 */
@SuppressWarnings("WeakerAccess")
class ViewHolderState extends LongSparseArray<ViewState> implements Sequenceable {
  public ViewHolderState() {
  }

  private ViewHolderState(int size) {
    super(size);
  }

  public boolean hasStateForHolder(EpoxyViewHolder holder) {
    return get(holder.getItemId()) != null;
  }

  public void save(Collection<EpoxyViewHolder> holders) {
    for (EpoxyViewHolder holder : holders) {
      save(holder);
    }
  }

  /** Save the state of the view bound to the given holder. */
  public void save(EpoxyViewHolder holder) {
    if (!holder.getModel().shouldSaveViewState()) {
      return;
    }

    // Reuse the previous sparse array if available. We shouldn't need to clear it since the
    // exact same view type is being saved to it, which
    // should have identical ids for all its views, and will just overwrite the previous state.
    ViewState state = get(holder.getItemId());
    if (state == null) {
      state = new ViewState();
    }

    state.save(holder.itemView);
    put(holder.getItemId(), state);
  }

  /**
   * If a state was previously saved for this view holder via {@link #save} it will be restored
   * here.
   */
  public void restore(EpoxyViewHolder holder) {
    if (!holder.getModel().shouldSaveViewState()) {
      return;
    }

    ViewState state = get(holder.getItemId());
    if (state != null) {
      state.restore(holder.itemView);
    }
  }

  @Override
  public boolean marshalling(Parcel parcel) {
    final int size = size();
    parcel.writeInt(size);
    for (int i = 0; i < size; i++) {
      parcel.writeLong(keyAt(i));
      parcel.writeSequenceable(valueAt(i));
    }
    return true;
  }

  @Override
  public boolean unmarshalling(Parcel parcel) {
    int size = parcel.readInt();
    setSize(size);
    for (int i = 0; i < size; i++) {
      long key = parcel.readLong();
      ViewState value = new ViewState();
      parcel.readSequenceable(value);
      put(key, value);
    }
    return true;
  }

  /**
   * A wrapper around a sparse array as a helper to save the state of a view. This also adds
   * parcelable support.
   */
  public static class ViewState extends SparseArray<Sequenceable> implements Sequenceable {

    public ViewState() {
    }

    private ViewState(int size, int[] keys, Sequenceable[] values) {
      super(size);
      for (int i = 0; i < size; ++i) {
        put(keys[i], values[i]);
      }
    }

    public void save(Component view) {
      int originalId = view.getId();
      setIdIfNoneExists(view);

//      view.saveHierarchyState(this);
      view.setId(originalId);
    }

    public void restore(Component view) {
      int originalId = view.getId();
      setIdIfNoneExists(view);

//      view.restoreHierarchyState(this);
      view.setId(originalId);
    }

    /**
     * If a view hasn't had an id set we need to set a temporary one in order to save state, since a
     * view won't save its state unless it has an id. The view's id is also the key into the sparse
     * array for its saved state, so the temporary one we choose just needs to be consistent between
     * saving and restoring state.
     */
    private void setIdIfNoneExists(Component view) {
      if (view.getId() == Component.ID_DEFAULT) {
        view.setId(22565/*view.getContext().getResourceManager().getElement(ResourceTable.Id_view_model_state_saving_id).getInteger()*/);
        /*try {
          view.setId(22565*//*view.getContext().getResourceManager().getElement(ResourceTable.Id_view_model_state_saving_id).getInteger()*//*);
        } catch (IOException e) {
          e.printStackTrace();
        } catch (NotExistException e) {
          e.printStackTrace();
        } catch (WrongTypeException e) {
          e.printStackTrace();
        }*/
      }
    }

    @Override
    public boolean marshalling(Parcel parcel) {
      int size = size();
      int[] keys = new int[size];
      Sequenceable[] values = new Sequenceable[size];
      for (int i = 0; i < size; ++i) {
        keys[i] = keyAt(i);
        values[i] = valueAt(i);
      }
      parcel.writeInt(size);
      parcel.writeIntArray(keys);
      parcel.writeSequenceableArray(values);
      return true;
    }

    @Override
    public boolean unmarshalling(Parcel parcel) {
      int size = parcel.readInt();
      int[] keys = new int[size];
      parcel.readIntArray(keys);
      Sequenceable[] values = new Sequenceable[size];
      parcel.readSequenceableArray(values);
      setSize(size);
      for (int i = 0; i < size; ++i) {
        put(keys[i], values[i]);
      }
      return true;
    }
  }
}
